/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.openfile; import java.beans.*; import java.io.*; import java.net.InetAddress; import java.text.MessageFormat; import java.util.*; import java.awt.*; import java.awt.event.*; import java.text.MessageFormat; import javax.swing.*; import javax.swing.event.*; import javax.swing.text.StyledDocument; import org.openide.*; import org.openide.cookies.*; import org.openide.filesystems.*; import org.openide.filesystems.FileSystem; import org.openide.loaders.*; import org.openide.nodes.*; import org.openide.text.NbDocument; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; /** Opens files when requested. Main functionality. * * @author Jaroslav Tulach, Jesse Glick */ class OpenFile extends Object { /** Open the file either by calling {@link OpenCookie} ({@link ViewCookie}), or by * showing it in the Explorer. * Uses {@link #find} to figure out what the right file object is. * @param f file on local disk * @param wait whether to wait until requested to return a status * @param addr address to send reply to, if waiting * @param port port to send reply to, if waiting * @param line line number to try to open to (starting at zero), or <code>-1</code> to ignore */ static void open (File f, final boolean wait, InetAddress addr, int port, int line) { FileObject fo = find (f); if (fo != null) { try { DataObject obj = DataObject.find (fo); final EditorCookie edit = line != -1 ? (EditorCookie) obj.getCookie (EditorCookie.class) : null; final OpenCookie open = (OpenCookie) obj.getCookie (OpenCookie.class); final ViewCookie view = (ViewCookie) obj.getCookie (ViewCookie.class); if (open != null || view != null || edit != null) { TopManager.getDefault ().setStatusText (SettingsBeanInfo.getString (wait ? "MSG_openingAndWaiting" : "MSG_opening", f.toString ())); if (edit != null) { edit.open (); StyledDocument doc = edit.openDocument (); JEditorPane[] panes = edit.getOpenedPanes (); if (panes.length > 0) panes[0].setCaretPosition (NbDocument.findLineOffset (doc, line)); else TopManager.getDefault ().setStatusText (SettingsBeanInfo.getString ("MSG_couldNotOpenAt")); } else if (open != null) { open.open (); } else { view.view (); } TopManager.getDefault ().setStatusText (""); // NOI18N if (wait) { // Could look for a SaveCookie just to see, but need not. Server.waitFor (obj, addr, port); } } else { Node n = obj.getNodeDelegate (); if (fo.isRoot ()) { // Try to get the node used in the usual Repository, which // has a non-blank display name and is thus nicer. FileSystem fs = fo.getFileSystem (); Node reponode = TopManager.getDefault ().getPlaces ().nodes ().repository (); Children repokids = reponode.getChildren (); Enumeration fsenum = repokids.nodes (); while (fsenum.hasMoreElements ()) { Node fsnode = (Node) fsenum.nextElement (); DataFolder df = (DataFolder) fsnode.getCookie (DataFolder.class); if (df != null && df.getPrimaryFile ().getFileSystem ().equals (fs)) { n = fsnode; break; } } } TopManager.getDefault ().getNodeOperation ().explore (n); if (wait) TopManager.getDefault ().notify (new NotifyDescriptor.Message (SettingsBeanInfo.getString ("MSG_cannotOpenWillClose", f))); } } catch (IOException ioe) { TopManager.getDefault ().notifyException (ioe); } } } /** Try to find the file object corresponding to a given file on disk. * Can produce a folder, mount directories, etc. as needed. * @param f the file on local disk * @return file object or <code>null</code> if not found */ private static synchronized FileObject find (File f) { String fileName = f.toString (); String fileNameUpper = fileName.toUpperCase (); // Handle ZIP/JAR files by mounting and displaying. if (fileNameUpper.endsWith (".ZIP") || fileNameUpper.endsWith (".JAR")) { // NOI18N JarFileSystem jfs = new JarFileSystem (); try { jfs.setJarFile (f); } catch (IOException e5) { TopManager.getDefault ().notifyException (e5); return null; } catch (PropertyVetoException e6) { TopManager.getDefault ().notifyException (e6); return null; } Repository repo2 = TopManager.getDefault ().getRepository (); FileSystem exist = repo2.findFileSystem (jfs.getSystemName ()); if (exist == null) { if (TopManager.getDefault ().notify (new NotifyDescriptor.Confirmation (SettingsBeanInfo.getString ("MSG_mountArchiveConfirm", f), SettingsBeanInfo.getString ("LBL_mountArchiveConfirm"))) .equals (NotifyDescriptor.OK_OPTION)) { repo2.addFileSystem (jfs); exist = jfs; } else { return null; } } // The root folder will be displayed in the Explorer: return exist.getRoot (); } // Next see if it is present in an existing LocalFileSystem. // enumeration of file systems Enumeration en = TopManager.getDefault ().getRepository ().getFileSystems (); while (en.hasMoreElements ()) { FileSystem fs = (FileSystem)en.nextElement (); if (fs instanceof LocalFileSystem) { LocalFileSystem lfs = (LocalFileSystem)fs; File root = lfs.getRootDirectory (); String rootName = root.toString ().toUpperCase (); if (fileNameUpper.startsWith (rootName)) { // the filesystem can contain the file String resource = fileName.substring (rootName.length ()).replace (File.separatorChar, '/'); if (resource.startsWith ("/")) // NOI18N resource = resource.substring (1); else if (resource.length () > 0) continue; // e.g. root = /tmp/foo but file = /tmp/foobar FileObject fo = fs.findResource (resource); if (fo != null) { return fo; } else { // Most likely, file was just created and is not yet in folder cache. Refresh each segment. FileObject currentPoint = fs.getRoot (); StringTokenizer resourceTok = new StringTokenizer (resource, "/"); // NOI18N String currentResource = ""; // NOI18N while (resourceTok.hasMoreTokens ()) { if (currentPoint == null || currentPoint.isData ()) { TopManager.getDefault ().notify (new NotifyDescriptor.Message (MessageFormat.format (NbBundle.getBundle (OpenFile.class).getString("MSG_no_file_in_root_nondir_comp"), new Object[] { fileName, root }))); return null; } else { currentPoint.refresh (); if (currentResource.length () > 0) currentResource += '/'; currentResource += resourceTok.nextToken (); currentPoint = fs.findResource (currentResource); } } if (currentPoint != null && currentPoint.isData ()) { return currentPoint; } else { TopManager.getDefault ().notify (new NotifyDescriptor.Message (MessageFormat.format (NbBundle.getBundle (OpenFile.class).getString ("MSG_no_file_in_root"), new Object[] { fileName, root }))); return null; } } } } } // Not found. For Java files, it is reasonable to mount the package root. String pkg = null; // packageKnown will only be true if // a .java is used and its package decl // indicates a real dir boolean packageKnown = false; if (fileNameUpper.endsWith (".JAVA")) { // NOI18N // Try to find the package name and then infer a directory to mount. BufferedReader rd = null; try { rd = new BufferedReader (new InputStreamReader (new FileInputStream (f))); scan: while (true) { String line = rd.readLine (); if (line == null) { packageKnown = true; // i.e. valid termination of search, default pkg break; } // Will not handle package statements broken across lines, oh well. if (line.indexOf ("package") == -1) continue; // NOI18N StringTokenizer tok = new StringTokenizer (line, " \t;"); // NOI18N boolean gotPackage = false; while (tok.hasMoreTokens ()) { String theTok = tok.nextToken (); if (gotPackage) { // Hopefully the package name, but first a sanity check... StringTokenizer ptok = new StringTokenizer (theTok, "."); // NOI18N boolean ok = ptok.hasMoreTokens (); while (ptok.hasMoreTokens ()) { String component = ptok.nextToken (); if (component.length () == 0) { ok = false; break; } if (! Character.isJavaIdentifierStart (component.charAt (0))) { ok = false; break; } for (int pos = 1; pos < component.length (); pos++) { if (! Character.isJavaIdentifierPart (component.charAt (pos))) { ok = false; break; } } } if (ok) { pkg = theTok; packageKnown = true; break scan; } else { // Keep on looking for valid package statement. gotPackage = false; continue; } } else if (theTok.equals ("package")) { // NOI18N gotPackage = true; } else if (theTok.equals ("{")) { // NOI18N // Most likely we can stop if hit opening brace of class def. // Usually people leave spaces around it. packageKnown = true; // valid end of search, default pkg break scan; } } } } catch (IOException e1) { TopManager.getDefault ().notifyException (e1); } finally { try { if (rd != null) rd.close (); } catch (IOException e2) { TopManager.getDefault ().notifyException (e2); } } } // Now try to go through the package name piece by piece and get the right parent directory. if (pkg == null) { pkg = ""; // assume default package // NOI18N } String prefix = pkg.replace ('.', File.separatorChar); File dir = f.getParentFile (); String pkgtouse = ""; // NOI18N while (! pkg.equals ("") && dir != null) { // NOI18N int lastdot = pkg.lastIndexOf ('.'); String trypkg; String trypart; if (lastdot == -1) { trypkg = ""; // NOI18N trypart = pkg; } else { trypkg = pkg.substring (0, lastdot); trypart = pkg.substring (lastdot + 1); } if (dir.getName ().equals (trypart) && dir.getParentFile () != null) { // Worked so far. dir = dir.getParentFile (); pkg = trypkg; if (pkgtouse.equals ("")) // NOI18N pkgtouse = trypart; else pkgtouse = trypart + "." + pkgtouse; // NOI18N } else { // No dice. packageKnown = false; break; } } // Ask what to mount (if anything). Prompt appropriately with the possible // mount points, as well as the recommended one if there is one (i.e. for valid *.java). File[] dirToMount = new File[] { null }; String[] mountPackage = new String[] { null }; int pkgLevel = 0; if (! pkgtouse.equals ("")) { // NOI18N int pos = -1; do { pos = pkgtouse.indexOf ('.', pos + 1); pkgLevel++; } while (pos != -1); } if (! packageKnown) pkgLevel = -1; askForMountPoint (f, pkgLevel, dirToMount, mountPackage); if (dirToMount[0] == null) return null; // Mount it. LocalFileSystem fs = new LocalFileSystem (); try { fs.setRootDirectory (dirToMount[0]); } catch (PropertyVetoException e3) { TopManager.getDefault ().notifyException (e3); return null; } catch (IOException e4) { TopManager.getDefault ().notifyException (e4); return null; } Repository repo = TopManager.getDefault ().getRepository (); if (repo.findFileSystem (fs.getSystemName ()) != null) { TopManager.getDefault ().notify (new NotifyDescriptor.Message (MessageFormat.format (NbBundle.getBundle (OpenFile.class).getString ("MSG_wasAlreadyMounted"), new Object[] { fs.getSystemName () }))); return null; } repo.addFileSystem (fs); return fs.findResource (mountPackage[0].replace ('.', '/') + (mountPackage[0].equals ("") ? "" : "/") + f.getName ()); // NOI18N } /** Ask what dir to mount to access a given file. * First may display a dialog asking whether the user wishes to select the default, * or edit the package selection. * @param f the file which should be accessible * @param pkgLevel the suggested depth of the package; 0 = default, 1 = single component, 2 = foo.bar, etc.; -1 if no suggested package * @param dirToMount 0th elt will contain the directory to mount (null to cancel the mount) * @param mountPackage 0th elt will contain the name of the package (possibly empty, not null) the file will be in */ private static void askForMountPoint (File f, int pkgLevel, final File[] dirToMount, final String[] mountPackage) { final Vector dirs = new Vector (); // list of mountable dir names; Vector<File> final Vector pkgs = new Vector (); // list of resulting package names; Vector<String> String pkg = ""; // NOI18N for (File dir = f.getParentFile (); dir != null; dir = dir.getParentFile ()) { dirs.add (dir); pkgs.add (pkg); if (! pkg.equals ("")) pkg = "." + pkg; // NOI18N pkg = dir.getName () + pkg; } // If no guess, always show full dialog. if (pkgLevel != -1) { String guessed = (String) pkgs.elementAt (pkgLevel); Object yesOption = new JButton (SettingsBeanInfo.getString ("LBL_quickMountYes")); Object noOption = new JButton (SettingsBeanInfo.getString ("LBL_quickMountNo")); Object result = TopManager.getDefault ().notify (new NotifyDescriptor ("".equals (guessed) ? // NOI18N SettingsBeanInfo.getString ("MSG_quickMountDefault", f.getName ()) : SettingsBeanInfo.getString ("MSG_quickMount", f.getName (), guessed), // message SettingsBeanInfo.getString ("LBL_quickMountTitle"), // title NotifyDescriptor.YES_NO_OPTION, // optionType NotifyDescriptor.QUESTION_MESSAGE, // messageType new Object[] { yesOption, noOption }, // options yesOption // initialValue )); if (result.equals (yesOption)) { dirToMount[0] = (File) dirs.elementAt (pkgLevel); mountPackage[0] = guessed; return; } else if (! result.equals (noOption)) { // Dialog closed--just stop everything. return; } } final JPanel panel = new JPanel (); panel.setLayout (new BorderLayout (0, 5)); panel.setBorder (new javax.swing.border.EmptyBorder (8, 8, 8, 8)); JTextArea textArea = new JTextArea (); textArea.setBackground (Color.lightGray); textArea.setFont (new Font ("SansSerif", Font.PLAIN, 11)); // NOI18N textArea.setText (SettingsBeanInfo.getString (pkgLevel == -1 ? "TXT_whereMountNoSuggest" : "TXT_whereMountSuggest", f.getName ())); textArea.setEditable (false); textArea.setLineWrap (true); textArea.setWrapStyleWord (true); panel.add (textArea, BorderLayout.NORTH); final JList list = new JList (pkgs); list.setVisibleRowCount (5); list.setSelectionMode (ListSelectionModel.SINGLE_SELECTION); if (pkgLevel != -1) list.setSelectedIndex (pkgLevel); list.setCellRenderer (new ListCellRenderer () { private Icon folderIcon = new ImageIcon (OpenFile.class.getResource ("folder.gif")); // NOI18N private Icon rootFolderIcon = new ImageIcon (OpenFile.class.getResource ("rootFolder.gif")); // NOI18N public Component getListCellRendererComponent (JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { String pkg2 = (String) value; JLabel lab = new JLabel (); if (pkg2.equals ("")) { // NOI18N lab.setText (SettingsBeanInfo.getString ("LBL_packageWillBeDefault")); lab.setIcon (rootFolderIcon); } else { lab.setText (SettingsBeanInfo.getString ("LBL_packageWillBe", pkg2)); lab.setIcon (folderIcon); } if (isSelected) { lab.setBackground (list.getSelectionBackground ()); lab.setForeground (list.getSelectionForeground ()); } else { lab.setBackground (list.getBackground ()); lab.setForeground (list.getForeground ()); } lab.setEnabled (list.isEnabled ()); lab.setFont (list.getFont ()); lab.setOpaque (true); return lab; } }); panel.add (new JScrollPane (list), BorderLayout.CENTER); // Name of mount point: final JLabel label = new JLabel (); label.setFont (new Font ("Monospaced", Font.PLAIN, 12)); // NOI18N panel.add (label, BorderLayout.SOUTH); panel.setPreferredSize (new Dimension (450, 300)); final JButton okButton = new JButton (SettingsBeanInfo.getString ("LBL_okButton")); JButton cancelButton = new JButton (SettingsBeanInfo.getString ("LBL_cancelButton")); list.addListSelectionListener (new ListSelectionListener () { public void valueChanged (ListSelectionEvent ev) { updateLabelEtcFromList (label, list, dirs, okButton); } }); updateLabelEtcFromList (label, list, dirs, okButton); final Dialog[] dialog = new Dialog[1]; dialog[0] = TopManager.getDefault ().createDialog (new DialogDescriptor (panel, // object SettingsBeanInfo.getString ("LBL_wizTitle"), // title true, // modal new Object[] { okButton, cancelButton }, // options okButton, // initial DialogDescriptor.DEFAULT_ALIGN, // align new HelpCtx (OpenFile.class.getName () + ".dialog"), // help // NOI18N new ActionListener () { // listener public void actionPerformed (ActionEvent evt) { if (evt.getSource () == okButton) { int idx = list.getSelectedIndex (); if (idx != -1) { dirToMount[0] = (File) dirs.elementAt (idx); mountPackage[0] = (String) pkgs.elementAt (idx); } else { System.err.println ("Should not have accepted OK button"); } } dialog[0].dispose (); } })); dialog[0].show (); } private static void updateLabelEtcFromList (JLabel label, JList list, Vector dirs, JButton okButton) { int idx = list.getSelectedIndex (); if (idx == -1) { label.setText (" "); // NOI18N okButton.setEnabled (false); } else { File dir = (File) dirs.elementAt (idx); label.setText (SettingsBeanInfo.getString ("LBL_dirWillBe", dir.getAbsolutePath ())); okButton.setEnabled (true); } } } /* * Log * 32 Gandalf 1.31 1/15/00 Jesse Glick Somewhat nicer * select-mount-point dialog (icons etc.). Also can close quickie dialog * to cancel whole open. * 31 Gandalf 1.30 1/15/00 Jesse Glick #5271 - opening multiple * files at once which share a new mount point. * 30 Gandalf 1.29 1/13/00 Jesse Glick NOI18N * 29 Gandalf 1.28 1/12/00 Jesse Glick I18N. * 28 Gandalf 1.27 1/7/00 Jesse Glick -line option for line * numbers. * 27 Gandalf 1.26 1/6/00 Jan Jancura Icon removed from * NotifyDesc. * 26 Gandalf 1.25 1/4/00 Jesse Glick Friendlier mount * dialogs. * 25 Gandalf 1.24 11/10/99 Jesse Glick Fixed race condition in * mount dialog. * 24 Gandalf 1.23 11/2/99 Jesse Glick Commented out testing * code. * 23 Gandalf 1.22 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 22 Gandalf 1.21 10/10/99 Petr Hamernik console debug messages * removed. * 21 Gandalf 1.20 8/27/99 Jesse Glick Fixed #3628--opening a * file which was just created on disk can fail. * 20 Gandalf 1.19 8/17/99 Jesse Glick Changed handling of * return status code to be more immediate and simplified. Fixes #2420 and * #3297. * 19 Gandalf 1.18 7/29/99 Ian Formanek Improved appearance * 18 Gandalf 1.17 7/19/99 Jesse Glick Fixed mount dialog to * use DialogDescriptor, not WizardDescriptor. * 17 Gandalf 1.16 7/10/99 Jesse Glick Open File module moved * to core. * 16 Gandalf 1.15 7/10/99 Jesse Glick Tweaks. * 15 Gandalf 1.14 7/10/99 Jesse Glick Mount-point dialog * works. * 14 Gandalf 1.13 7/10/99 Jesse Glick Changing the mounting * algorithm. * 13 Gandalf 1.12 7/10/99 Jesse Glick Splitting server from * opening functionality, etc. * 12 Gandalf 1.11 7/10/99 Jesse Glick Sundry clean-ups (mostly * bundle usage). * 11 Gandalf 1.10 6/26/99 Jesse Glick * 10 Gandalf 1.9 6/25/99 Jesse Glick Installing Open File * menu item in File menu. * 9 Gandalf 1.8 6/9/99 Ian Formanek ---- Package Change To * org.openide ---- * 8 Gandalf 1.7 5/25/99 Jesse Glick Comments. * 7 Gandalf 1.6 5/25/99 Jesse Glick Added -wait. * 6 Gandalf 1.5 5/25/99 Jaroslav Tulach Waits for notification * that the open command succeeded. * 5 Gandalf 1.4 5/22/99 Jesse Glick Licenses. * 4 Gandalf 1.3 5/22/99 Jesse Glick Handling options, and * doc. * 3 Gandalf 1.2 5/22/99 Jesse Glick Support for opening * archive files, and also better display for root folders. * 2 Gandalf 1.1 5/22/99 Jesse Glick If Java file does not * exist in mounted fs, tries to mount it in the correct package. * 1 Gandalf 1.0 5/19/99 Jesse Glick * $ */